#include "GamePlayerClient.h"

string GamePlayerClient::DEFAULT_SERVER_HOST = "127.0.0.1";
string GamePlayerClient::DEFAULT_SERVER_PORT = "8080";

GamePlayerClient::GamePlayerClient(string name)
{
	eventInputQueue = new queue<GameEvent*>;
	_isConnected = false;
	isSend = false;
	isReceive = false;
	_isInitialized = false;
	_isSceneInitialized = false;
	this->name = name;
	backgroundFile = ".\\temp\\";
	serverHost = DEFAULT_SERVER_HOST;
	serverPort = DEFAULT_SERVER_PORT;
	this->connectError = false;
	eventOutputQueueLock = new CriticalSection();
	eventInputQueueLock = new CriticalSection();
}

GamePlayerClient::GamePlayerClient(string name, string port)
{
	eventInputQueue = new queue<GameEvent*>;
	_isConnected = false;
	isSend = false;
	isReceive = false;
	_isInitialized = false;
	_isSceneInitialized = false;
	this->name = name;
	backgroundFile = ".\\temp\\";
	serverHost = DEFAULT_SERVER_HOST;
	serverPort = port;
	this->connectError = false;
	eventOutputQueueLock = new CriticalSection();
	eventInputQueueLock = new CriticalSection();
}

GamePlayerClient::GamePlayerClient(string name, string host, string port)
{
	_isConnected = false;
	eventInputQueue = new queue<GameEvent*>;
	isSend = false;
	this->connectError = false;
	isReceive = false;
	_isInitialized = false;
	_isSceneInitialized = false;
	this->name = name;
	backgroundFile = ".\\temp\\";
	serverHost = host;
	serverPort = port;
	eventOutputQueueLock = new CriticalSection();
	eventInputQueueLock = new CriticalSection();
}

GamePlayerClient::~GamePlayerClient()
{
	delete eventInputQueue;
	delete eventOutputQueueLock;
	delete eventInputQueueLock;
}

int GamePlayerClient::connectToServer(void)
{
	int connectResult=-2;
	cout << "Connecting to server..." << endl;
	if (!_isConnected)
	{
		socket = Winsock2SocketFactory::getInstance()->createClientSocket(serverHost, serverPort);
		Log::getInstance()->info(socket->toString());
		try
		{
			socket->doConnect();
			_isConnected = true;
		}
		catch (NetworkException& ne)
		{
			cout << "Unable to connect to server: " << ne.getMessage() << endl;
		}
	}
	return 0;
}

bool GamePlayerClient::isConnected(void)
{
	return _isConnected;
}

void GamePlayerClient::beginSession(void)
{
	cout << "Beginning session..." << endl;
	GameEvent* gameEvent = new GameEvent(EVENT_GAME_SESSION_BEGIN, this->name);
	cout << "Sending event to start session: " << gameEvent->marshal()->get() << endl;
	int sendResult = socket->doSend(gameEvent->marshal()->get());
	if (sendResult == SOCKET_ERROR)
	{
		cout << "Error sending event to start session: " << WSAGetLastError() << endl;
	}
	else
	{
		receiveGameFiles();
		receiveSceneState();
	}
}

void GamePlayerClient::endSession(void)
{
	cout << "Ending session..." << endl;
	GameEvent* gameEvent = new GameEvent(EVENT_GAME_SESSION_END, "");
	cout << "Sending event to end session: " << gameEvent->marshal()->get() << endl;
	int sendResult = socket->doSend(gameEvent->marshal()->get());
	if (sendResult == SOCKET_ERROR)
	{
		cout << "Error sending event to end session: " << WSAGetLastError() << endl;
	}
}

void GamePlayerClient::receiveGameFiles(void)
{
	char recvbuf[2048];
    int recvbuflen = 2048;
	int receiveResult;
	string bytes = "";
	string command = "";
	string filename = "";
	string startFileCommand = "STARTFILE";
	string endFileCommand = "ENDFILE";
	ofstream file;

	bool isSendingFile = false;
	int totalReceived = 0;

	receiveResult = socket->doReceive(recvbuf, recvbuflen);
	command = bytes.append(recvbuf).substr(0, startFileCommand.length());
	if (command.compare(startFileCommand) == 0)
	{
		cout << "Received STARTFILE command." <<  endl;
		receiveResult = socket->doReceive(recvbuf, recvbuflen);
		bytes = "";
		filename = bytes.append(recvbuf).substr(0, receiveResult);
		cout << "Received filename " << filename <<  endl;
		file.open(backgroundFile.append(filename), ios::out | ios::trunc | ios::binary);
		if (file.is_open())
		{
			do
			{
				isSendingFile = true;
				receiveResult = socket->doReceive(recvbuf, recvbuflen);
				if (receiveResult > 0)
				{
					cout << "Received " << receiveResult << " bytes." << endl;
					totalReceived += receiveResult;

					bytes = "";
					command = bytes.append(recvbuf).substr(0, endFileCommand.length());
					if (command.compare(endFileCommand) == 0)
					{
						cout << "Received ENDFILE command." <<  endl;
						file.flush();
						file.close();
						isSendingFile = false;
						_isInitialized = true;
					}
					file.write(recvbuf, receiveResult);
				}
				else if (receiveResult == 0)
				{
					cout << "Connection closed" << endl;
				}
				else
				{
					cout << "recv failed with error: " << WSAGetLastError() << endl;
				}
			} while (receiveResult > 0 && isSendingFile);
		}
	}

	if (file.is_open())
	{
		cout << "There was an error that prevented the file to be closed." << endl;
	}
	cout << "Total received: " << totalReceived << endl;
}

void GamePlayerClient::receiveSceneState(void)
{char recvbuf[1024];
    int recvbuflen = 1024;
	int receiveResult;
	char* pBuf;
	string bytes = "";
	string command = "";
	string startFileCommand = "SCENERECIBIDO";
	string endFileCommand = "ENDSCENE";
	ByteBuffer* buffer = new ByteBuffer();
	bool isSendingScene = false;
	int totalReceived = 0;
	int dataReceived =0;
	char bufferRead[1024];
	
	receiveResult = socket->doReceive(recvbuf, recvbuflen);
	//memcpy(sendBuffer, readBuffer+(totalBytesSent), bytesToSend);
	pBuf= recvbuf;
	int sceneSize = atoi(pBuf);
	cout << "Received scene size:" << sceneSize<<  endl;
	stringstream ss;
	ss << sceneSize;	
	int sizeOfSize=(int)strlen(ss.str().c_str());
	pBuf+= sizeOfSize;
	dataReceived = receiveResult-sizeOfSize;
	isSendingScene=true;
	if (dataReceived >0){
			buffer->put(pBuf, dataReceived);
			if ( (dataReceived) == sceneSize)
				isSendingScene=false;
	}
	while(receiveResult > 0 && isSendingScene ){
			receiveResult = socket->doReceive(recvbuf, recvbuflen);
			pBuf=recvbuf;
			dataReceived += receiveResult;
			buffer->put(pBuf, dataReceived);
			
		
		if ( (dataReceived) == sceneSize)
			isSendingScene=false;
	}
	//command = bytes.append(recvbuf).substr(0, startFileCommand.length());
/*
	if (command.compare(startFileCommand) == 0)
	{
		cout << "Received STARTSCENE command." <<  endl;
		do
			{
				isSendingScene = true;
				receiveResult = socket->doReceive(recvbuf, recvbuflen);
				if (receiveResult > 0)
				{
					cout << "Received " << receiveResult << " bytes." << endl;
					

					bytes = "";
					command = bytes.append(recvbuf).substr(0, endFileCommand.length());
					if (command.compare(endFileCommand) == 0)
					{
						cout << "Received ENDSCENE command." <<  endl;
						isSendingScene = false;
						_isSceneInitialized = true;
					}
					if(!_isSceneInitialized){
						buffer->put(recvbuf, receiveResult);
					totalReceived += receiveResult;
					}
				}
				else if (receiveResult == 0)
				{
					cout << "Connection closed" << endl;
				}
				else
				{
					cout << "recv failed with error: " << WSAGetLastError() << endl;
				}
			} while (receiveResult > 0 && isSendingScene);
		
	}*/
	cout << "Total received: " << dataReceived << endl;


	//char recvbuf[1024];
 //   int recvbuflen = 1024;
	//ByteBuffer* buffer = new ByteBuffer();
	//int receiveResult;
	//int totalReceived = 0;
	//bool isSendingFile = false;
	//receiveResult = socket->doReceive(recvbuf, recvbuflen);
	//if (receiveResult > 0)
 //   {
	//	buffer->put(recvbuf, receiveResult);
	//	cout << "Received " << receiveResult << " bytes." << endl;
//}
	this->sceneBuff = buffer;
	socket->doSend(startFileCommand.c_str());
		cout << "Sent SCENESTate confirmation" << endl;
	Sleep(200);
	}
CriticalSection* GamePlayerClient::getEventInputQueueLock()
{
	return eventInputQueueLock;
}

void GamePlayerClient::setEventInputQueueLock(CriticalSection* eventInputQueueLock)
{
	this->eventInputQueueLock = eventInputQueueLock;
}
	
queue<GameEvent*>* GamePlayerClient::getEventInputQueue()
{
	return eventInputQueue;
}

void GamePlayerClient::setEventInputQueue(queue<GameEvent*>* eventInputQueue)
{
	this->eventInputQueue = eventInputQueue;
}

int GamePlayerClient::startSendingUpdates(void)
{	char tempBuff[1024];
	int cantEvent=0;
	int eventSize;
	isSend = true;
	int sendResult;
	GameEvent* gameEvent;
	while (isSend)
	{	ByteBuffer b1;
		ByteBuffer b2;
		cantEvent=0;
		eventOutputQueueLock->adquire();
		//while(!eventOutputQueue.empty() && cantEvent <4 ){
		if (!eventOutputQueue.empty())
		{
			gameEvent = eventOutputQueue.front();
			eventOutputQueue.pop();
			b2.put(gameEvent->marshal()->get());
			eventSize =b2.size();

			cantEvent++;
			delete gameEvent;
		}
		else
		{
			gameEvent = 0;
		//}
		}
		eventOutputQueueLock->release();

		if (cantEvent != 0)
		{
		/*stringstream ss;
		ss << cantEvent;	
		b1.put(ss.str().c_str());*/
		stringstream ss;
		ss << eventSize;	
		b1.put(ss.str().c_str());
		b1.put(b2.get());
		cout << "Sending events " << b1.getAll() << " from client " << name << "." << endl;
			//cout << "Sending event " << gameEvent->toString() << " from client " << name << "." << endl;
			//sendResult = socket->doSend(gameEvent->marshal()->get());
			sendResult = socket->doSend(b1.get());
			if (sendResult == SOCKET_ERROR)
			{
				cout << "send failed with error: " << WSAGetLastError() << endl;
				this->connectError=true;
			}
			else
			{
				cout << "Bytes Sent: " << sendResult << endl;
			}
			
		}
	
	
	}
	return 0;
}

int GamePlayerClient::disconnectFromServer(void)
{
	socket->doShutdown();
	socket->doClose();
    return 0;
}

string GamePlayerClient::getBackgroundFile(void)
{
	return backgroundFile;
}

void GamePlayerClient::setBackgroundFile(string path)
{
	this->backgroundFile = path;
}

bool GamePlayerClient::isInitialized(void)
{
	return _isInitialized;
}

int GamePlayerClient::startReceivingUpdates(void)
{
	isReceive = true;
	char recvbuf[1024];
    int recvbuflen = 1024;
	int receiveResult = 0;

    do
    {
		if (isReceive)
		{
			receiveResult = socket->doReceive(recvbuf, recvbuflen);
			cout << "Received: " << receiveResult << " bytes." << endl;
			if (receiveResult > 0)
			{
				ByteBuffer* buffer = new ByteBuffer();
				buffer->put(recvbuf, receiveResult);
				cout << "Data received: " << buffer->getAll() << "." << endl;
				GameEvent* event = new GameEvent();
				event->unmarshal(buffer);
				eventInputQueueLock->adquire();
				eventInputQueue->push(event);
				eventInputQueueLock->release();
				cout << "Event received: " << event->toString() << endl;
				delete buffer;
			}
			else if (receiveResult == 0)
			{
				cout << "Connection closed" << endl;
			}
			else
			{
				int errorCode = WSAGetLastError();
				 cout << "recv failed with error: " << errorCode << endl;
				if (errorCode == 10054 || errorCode == 10053)
				{
					this->connectError=true;
				}
			}
		}
    } while (receiveResult > 0);

	return 0;
}

void GamePlayerClient::sendUpdate(GameEvent* event)
{
	eventOutputQueueLock->adquire();
	eventOutputQueue.push(event);
	eventOutputQueueLock->release();
}

bool GamePlayerClient::hasUpdates(void)
{
	return !eventInputQueue->empty();
}

GameEvent* GamePlayerClient::recieveUpdates(void)
{
    eventInputQueueLock->adquire();
    GameEvent* event = eventInputQueue->front();
    eventInputQueue->pop();
    eventInputQueueLock->release();
    return event;
}

void GamePlayerClient::stopReceivingUpdates(void)
{
	isReceive = false;
}

void GamePlayerClient::stopSendingUpdates(void)
{
	isSend = false;
}

void GamePlayerClient::getInfo(struct addrinfo* result)
{
	int i = 1;
	struct addrinfo* ptr = NULL;
	string info = "";
	stringstream ss;
	for(ptr=result; ptr != NULL ;ptr=ptr->ai_next)
	{
		ss << ptr->ai_flags;
		ss.str();
		info.append("Flags: ").append(ss.str());
		ss.clear();
        info.append("; Family: ");
        switch (ptr->ai_family)
		{
            case AF_UNSPEC:
                info.append("Unspecified");
                break;
            case AF_INET:
                info.append("AF_INET (IPv4)");
                break;
            case AF_INET6:
                info.append("AF_INET6 (IPv6)");
                break;
            case AF_NETBIOS:
                info.append("AF_NETBIOS (NetBIOS)");
                break;
            default:
				ss << ptr->ai_family;
				info.append("Other: ").append(ss.str());
				ss.clear();
                break;
        }
        info.append("; Socket type: ");
        switch (ptr->ai_socktype) {
            case 0:
                info.append("Unspecified");
                break;
            case SOCK_STREAM:
                info.append("SOCK_STREAM (stream)");
                break;
            case SOCK_DGRAM:
                info.append("SOCK_DGRAM (datagram)");
                break;
            case SOCK_RAW:
                info.append("SOCK_RAW (raw)");
                break;
            case SOCK_RDM:
                info.append("SOCK_RDM (reliable message datagram)");
                break;
            case SOCK_SEQPACKET:
                info.append("SOCK_SEQPACKET (pseudo-stream packet)");
                break;
            default:
				ss << ptr->ai_socktype;
				info.append("Other: ").append(ss.str());
				ss.clear();
                break;
        }
        info.append("; Protocol: ");
        switch (ptr->ai_protocol) {
            case 0:
                info.append("Unspecified");
                break;
            case IPPROTO_TCP:
                info.append("IPPROTO_TCP (TCP)");
                break;
            case IPPROTO_UDP:
                info.append("IPPROTO_UDP (UDP)");
                break;
            default:
				ss << ptr->ai_protocol;
				info.append("Other: ").append(ss.str());
				ss.clear();
                break;
        }
		ss << ptr->ai_addrlen;
		info.append("; Length of this sockaddr: ").append(ss.str());
		ss.clear();
		if (ptr->ai_canonname != 0)
		{
			info.append("; Canonical name: ").append(ptr->ai_canonname);
		}
    }
	cout << "CLIENT INFO: " << info << endl;
}

string GamePlayerClient::getName(void)
{
	return name;
}

bool GamePlayerClient::connectionError(){
	return this->connectError;
}

ByteBuffer* GamePlayerClient::getSceneBuff(){
	return this->sceneBuff;
}